//----------------------------------------------------------------
//module name : yhz_execute
//engineer : yhz
//date : 2021.07.28
//----------------------------------------------------------------
`include "yhz_defines.v"
module yhz_execute (
    input  wire        i_clk             ,
    input  wire        i_rst             ,
    input  wire        i_pipeline_unlock ,
    input  wire        i_pipeline_pulse  ,
    output wire        o_pipeline_pulse  ,
    //command
    input  wire [7:0]  i_inst_encode     ,
    input  wire [63:0] i_inst_op1        ,
    input  wire [63:0] i_inst_op2        ,
    input  wire [63:0] i_inst_op3        ,
    //transfer
    input  wire        i_w_rd_en         ,
    input  wire [4:0]  i_w_rd_addr       ,
    output wire [63:0] o_w_rd_data_t     ,
    //to_memory
    output wire        o_load_en         ,
    output wire        o_store_en        ,
    output wire [4:0]  o_load_cmd        ,
    output wire [63:0] o_store_data      ,
    output wire [63:0] o_store_mask      ,
    output wire        o_w_rd_en         ,
    output wire [4:0]  o_w_rd_addr       ,
    output wire [63:0] o_w_rd_data        
);
//----------------------------------------------------------------
//register & wire
//----------------------------------------------------------------
    //transfer
    reg  [63:0] w_rd_data_t    ;
    reg  [4:0]  load_cmd_t     ;
    reg  [63:0] store_mask_t   ;
    //register
    reg         pipeline_pulse ;
    reg         load_en        ;
    reg         store_en       ;
    reg  [4:0]  load_cmd       ;
    reg  [63:0] store_data     ;
    reg  [63:0] store_mask     ;
    reg         w_rd_en        ;
    reg  [4:0]  w_rd_addr      ;
    reg  [63:0] w_rd_data      ;
    //64
    wire [63:0] slliw_data = i_inst_op1                <<  i_inst_op2[4:0] ;
    wire [31:0] srliw_data = i_inst_op1[31:0]          >>  i_inst_op2[4:0] ;
    wire [31:0] sraiw_data = $signed(i_inst_op1[31:0]) >>> i_inst_op2[4:0] ;
    wire [63:0] addw_data  = i_inst_op1                +   i_inst_op2      ;
    wire [63:0] subw_data  = i_inst_op1                -   i_inst_op2      ;
    wire [31:0] sllw_data  = i_inst_op1[31:0]          <<  i_inst_op2[4:0] ;
    wire [31:0] srlw_data  = i_inst_op1[31:0]          >>  i_inst_op2[4:0] ;
    wire [31:0] sraw_data  = $signed(i_inst_op1[31:0]) >>> i_inst_op2[4:0] ;
    //transfer
    wire load_en_t  = (((i_inst_encode > 8'd10) && (i_inst_encode < 8'd16)) || 
                       ((i_inst_encode > 8'd40) && (i_inst_encode < 8'd43))) ? 1'b1 : 1'b0 ;
    wire store_en_t = (((i_inst_encode > 8'd15) && (i_inst_encode < 8'd19)) || 
                        (i_inst_encode == 8'd43)) ? 1'b1 : 1'b0 ;
//----------------------------------------------------------------
//logic
//----------------------------------------------------------------
    //transfer
    //w_rd_data_t
    always @(*) begin
        if(i_rst) begin
            w_rd_data_t = 64'd0 ;
        end
        else begin
            case(i_inst_encode)
                //jump
                `LUI : begin
                    w_rd_data_t = i_inst_op2 ;
                end
                `AUIPC : begin
                    w_rd_data_t = i_inst_op2 + i_inst_op3 ;
                end
                `JAL,`JALR    : begin
                    w_rd_data_t = i_inst_op3 + 64'd4 ;
                end
                //memory
                `LB,`LH,`LW,`LBU,`LHU,`LWU,`LD : begin
                    w_rd_data_t = i_inst_op1 + i_inst_op2 ;
                end
                `SB,`SH,`SW,`SD : begin
                    w_rd_data_t = i_inst_op1 + i_inst_op3 ;
                end
                //math
                `ADDI,`ADD : begin
                    w_rd_data_t = i_inst_op1 + i_inst_op2 ;
                end
                `ADDIW,`ADDW : begin    //64
                    w_rd_data_t = {{32{addw_data[31]}} , addw_data[31:0]} ;
                end
                `SUB : begin
                    w_rd_data_t = i_inst_op1 - i_inst_op2 ;
                end
                `SUBW : begin    //64
                    w_rd_data_t = {{32{subw_data[31]}} , subw_data[31:0]} ;
                end
                //logic
                `SLTI,`SLT : begin
                    case({i_inst_op1[63],i_inst_op2[63]})
                        2'b00 : w_rd_data_t = (i_inst_op1 < i_inst_op2)? 64'd1 : 64'd0 ;
                        2'b01 : w_rd_data_t = 64'd0 ;
                        2'b10 : w_rd_data_t = 64'd1 ;
                        2'b11 : w_rd_data_t = ((~i_inst_op1 + 64'd1) > (~i_inst_op2 + 64'd1))? 64'd1 : 64'd0 ;
                        default : w_rd_data_t = `NO_USE ;
                    endcase
                end
                `SLTIU,`SLTU : begin
                    w_rd_data_t = (i_inst_op1 < i_inst_op2)? 64'd1 : 64'd0 ;
                end
                `XORI,`XOR : begin
                    w_rd_data_t = i_inst_op1 ^ i_inst_op2 ;
                end
                `ORI,`OR : begin
                    w_rd_data_t = i_inst_op1 | i_inst_op2 ;
                end
                `ANDI,`AND : begin
                    w_rd_data_t = i_inst_op1 & i_inst_op2 ;
                end
                `SLLI,`SLL : begin    //64_changed
                    w_rd_data_t = i_inst_op1 << i_inst_op2[5:0] ;
                end
                `SLLIW : begin    //64
                    w_rd_data_t = {{32{slliw_data[31]}} , slliw_data[31:0]} ;
                end
                `SLLW : begin    //64
                    w_rd_data_t = {{32{sllw_data[31]}} , sllw_data} ;
                end
                `SRLI,`SRL : begin    //64_changed
                    w_rd_data_t = i_inst_op1 >> i_inst_op2[5:0] ;
                end
                `SRLIW : begin    //64
                    w_rd_data_t = {{32{srliw_data[31]}} , srliw_data} ;
                end
                `SRLW : begin    //64
                    w_rd_data_t = {{32{srlw_data[31]}} , srlw_data} ;
                end
                `SRAI,`SRA : begin    //64_changed
                    w_rd_data_t = $signed(i_inst_op1) >>> i_inst_op2[5:0] ;
                end
                `SRAIW : begin    //64
                    w_rd_data_t = {{32{sraiw_data[31]}} , sraiw_data} ;
                end
                `SRAW : begin    //64
                    w_rd_data_t = {{32{sraw_data[31]}} , sraw_data} ;
                end
                //csr
                `CSRRW,`CSRRS,`CSRRC,`CSRRWI,`CSRRSI,`CSRRCI : begin
                    w_rd_data_t = i_inst_op2 ;
                end
                default : w_rd_data_t = 64'd0 ;
            endcase
        end
    end
    //load_cmd_t
    always @(*) begin
        if(i_rst) begin
            load_cmd_t = 5'd0 ;
        end
        else begin
            case(i_inst_encode)
                `LB  : load_cmd_t = 5'b00011 ;
                `LH  : load_cmd_t = 5'b00101 ;
                `LW  : load_cmd_t = 5'b01001 ;
                `LBU : load_cmd_t = 5'b00010 ;
                `LHU : load_cmd_t = 5'b00100 ;
                `LWU : load_cmd_t = 5'b01000 ;
                `LD  : load_cmd_t = 5'b10001 ;
                default : load_cmd_t = 5'd0 ;
            endcase
        end
    end
    //store_mask_t
    always @(*) begin
        if(i_rst) begin
            store_mask_t = 64'd0 ;
        end
        else begin
            case(i_inst_encode)
                `SB : store_mask_t = {56'd0 , 8'hff       } ;
                `SH : store_mask_t = {48'd0 , 16'hffff    } ;
                `SW : store_mask_t = {32'd0 , 32'hffffffff} ;
                `SD : store_mask_t = 64'hffffffffffffffff   ;
                default : store_mask_t = 64'd0 ;
            endcase
        end
    end
    //register
    //pipeline_pulse
    always @(posedge i_clk) begin
        if(i_rst) begin
            pipeline_pulse <= 1'b0 ;
        end
        else if(i_pipeline_unlock) begin
            pipeline_pulse <= 1'b0 ;
        end
        else begin
            pipeline_pulse <= i_pipeline_pulse ;
        end
    end
    //load_en
    always @(posedge i_clk) begin
        if(i_rst) begin
            load_en <= 1'b0 ;
        end
        else if(i_pipeline_unlock) begin
            load_en <= 1'b0 ;
        end
        else if(pipeline_pulse & (!i_pipeline_unlock)) begin
            load_en <= load_en ;
        end
        else begin
            load_en <= load_en_t ;
        end
    end
    //store_en
    always @(posedge i_clk) begin
        if(i_rst) begin
            store_en <= 1'b0 ;
        end
        else if(i_pipeline_unlock) begin
            store_en <= 1'b0 ;
        end
        else if(pipeline_pulse & (!i_pipeline_unlock)) begin
            store_en <= store_en ;
        end
        else begin
            store_en <= store_en_t ;
        end
    end
    //load_cmd
    always @(posedge i_clk) begin
        if(i_rst) begin
            load_cmd <= 5'd0 ;
        end
        else if(i_pipeline_unlock) begin
            load_cmd <= 5'd0 ;
        end
        else if(pipeline_pulse & (!i_pipeline_unlock)) begin
            load_cmd <= load_cmd ;
        end
        else begin
            load_cmd <= load_cmd_t ;
        end
    end
    //store_data
    always @(posedge i_clk) begin
        if(i_rst) begin
            store_data <= 64'd0 ;
        end
        else if(i_pipeline_unlock) begin
            store_data <= 64'd0 ;
        end
        else if(pipeline_pulse & (!i_pipeline_unlock)) begin
            store_data <= store_data ;
        end
        else begin
            store_data <= i_inst_op2 ;
        end
    end
    //store_mask
    always @(posedge i_clk) begin
        if(i_rst) begin
            store_mask <= 64'd0 ;
        end
        else if(i_pipeline_unlock) begin
            store_mask <= 64'd0 ;
        end
        else if(pipeline_pulse & (!i_pipeline_unlock)) begin
            store_mask <= store_mask ;
        end
        else begin
            store_mask <= store_mask_t ;
        end
    end
    //w_rd_en
    always @(posedge i_clk) begin
        if(i_rst) begin
            w_rd_en <= 1'b0 ;
        end
        else if(i_pipeline_unlock) begin
            w_rd_en <= 1'b0 ;
        end
        else if(pipeline_pulse & (!i_pipeline_unlock)) begin
            w_rd_en <= w_rd_en ;
        end
        else begin
            w_rd_en <= i_w_rd_en ;
        end
    end
    //w_rd_addr
    always @(posedge i_clk) begin
        if(i_rst) begin
            w_rd_addr <= 5'd0 ;
        end
        else if(i_pipeline_unlock) begin
            w_rd_addr <= 5'd0 ;
        end
        else if(pipeline_pulse & (!i_pipeline_unlock)) begin
            w_rd_addr <= w_rd_addr ;
        end
        else begin
            w_rd_addr <= i_w_rd_addr ;
        end
    end
    //w_rd_data
    always @(posedge i_clk) begin
        if(i_rst) begin
            w_rd_data <= 64'd0 ;
        end
        else if(i_pipeline_unlock) begin
            w_rd_data <= 64'd0 ;
        end
        else if(pipeline_pulse & (!i_pipeline_unlock)) begin
            w_rd_data <= w_rd_data ;
        end
        else begin
            w_rd_data <= w_rd_data_t ;
        end
    end
//----------------------------------------------------------------
//output
//----------------------------------------------------------------
    assign o_pipeline_pulse = pipeline_pulse ;
    assign o_w_rd_data_t    = w_rd_data_t    ;
    assign o_load_en        = load_en        ;
    assign o_store_en       = store_en       ;
    assign o_load_cmd       = load_cmd       ;
    assign o_store_data     = store_data     ;
    assign o_store_mask     = store_mask     ;
    assign o_w_rd_en        = w_rd_en        ;
    assign o_w_rd_addr      = w_rd_addr      ;
    assign o_w_rd_data      = w_rd_data      ;
//----------------------------------------------------------------
endmodule
//----------------------------------------------------------------
